"""EDA数据可视化工具

提供EDA数据和分析结果的可视化功能，支持：
- 原始信号和预处理信号对比
- 峰值检测结果展示
- 频域分析图表
- 质量评估可视化
- 批量结果统计图表

主要函数：
- plot_eda_signal: 绘制EDA信号
- plot_peak_detection: 绘制峰值检测结果
- plot_frequency_analysis: 绘制频域分析
- plot_quality_assessment: 绘制质量评估
- create_analysis_report: 创建完整分析报告
"""

import logging
from pathlib import Path
from typing import Dict, Any, List, Optional, Union, Tuple
import numpy as np
import pandas as pd
from datetime import datetime

# 可视化库导入（带错误处理）
try:
    import matplotlib.pyplot as plt
    import matplotlib.patches as patches
    from matplotlib.gridspec import GridSpec
    import seaborn as sns
    MATPLOTLIB_AVAILABLE = True
except ImportError:
    MATPLOTLIB_AVAILABLE = False
    plt = None
    sns = None

try:
    import plotly.graph_objects as go
    import plotly.subplots as sp
    from plotly.offline import plot as plotly_plot
    PLOTLY_AVAILABLE = True
except ImportError:
    PLOTLY_AVAILABLE = False
    go = None
    sp = None

logger = logging.getLogger(__name__)

# 设置中文字体和样式
if MATPLOTLIB_AVAILABLE:
    plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
    plt.rcParams['axes.unicode_minus'] = False
    sns.set_style("whitegrid")
    sns.set_palette("husl")

class EDAVisualizer:
    """EDA数据可视化器"""
    
    def __init__(self, 
                 figsize: Tuple[int, int] = (12, 8),
                 dpi: int = 100,
                 style: str = 'seaborn',
                 use_plotly: bool = False):
        """初始化可视化器
        
        Args:
            figsize: 图形大小
            dpi: 图形分辨率
            style: 绘图样式
            use_plotly: 是否使用Plotly（交互式图表）
        """
        self.figsize = figsize
        self.dpi = dpi
        self.style = style
        self.use_plotly = use_plotly and PLOTLY_AVAILABLE
        
        if not MATPLOTLIB_AVAILABLE and not PLOTLY_AVAILABLE:
            logger.error("未安装可视化库（matplotlib或plotly），无法生成图表")
            raise ImportError("需要安装matplotlib或plotly库")
        
        if MATPLOTLIB_AVAILABLE and not self.use_plotly:
            plt.style.use(style if style in plt.style.available else 'default')
    
    def plot_eda_signal(self, 
                       eda_data: Dict[str, Any],
                       results: Dict[str, Any] = None,
                       show_preprocessing: bool = True,
                       time_range: Tuple[float, float] = None,
                       save_path: str = None) -> Optional[str]:
        """绘制EDA信号图
        
        Args:
            eda_data: EDA数据字典
            results: 处理结果字典
            show_preprocessing: 是否显示预处理对比
            time_range: 时间范围 (start, end) 秒
            save_path: 保存路径
            
        Returns:
            保存的文件路径（如果保存）
        """
        if self.use_plotly:
            return self._plot_eda_signal_plotly(eda_data, results, show_preprocessing, time_range, save_path)
        else:
            return self._plot_eda_signal_matplotlib(eda_data, results, show_preprocessing, time_range, save_path)
    
    def _plot_eda_signal_matplotlib(self, eda_data, results, show_preprocessing, time_range, save_path):
        """使用Matplotlib绘制EDA信号"""
        signal = eda_data['signal']
        sampling_rate = eda_data.get('sampling_rate', 4.0)
        
        # 创建时间轴
        time_axis = np.arange(len(signal)) / sampling_rate
        
        # 应用时间范围
        if time_range:
            start_idx = int(time_range[0] * sampling_rate)
            end_idx = int(time_range[1] * sampling_rate)
            time_axis = time_axis[start_idx:end_idx]
            signal = signal[start_idx:end_idx]
        
        # 创建图形
        if show_preprocessing and results and 'preprocessed_signal' in results:
            fig, (ax1, ax2) = plt.subplots(2, 1, figsize=self.figsize, dpi=self.dpi)
            
            # 原始信号
            ax1.plot(time_axis, signal, 'b-', linewidth=1, alpha=0.7, label='原始信号')
            ax1.set_title('原始EDA信号', fontsize=14, fontweight='bold')
            ax1.set_ylabel('电导值 (μS)', fontsize=12)
            ax1.grid(True, alpha=0.3)
            ax1.legend()
            
            # 预处理信号
            preprocessed = results['preprocessed_signal']
            if time_range:
                preprocessed = preprocessed[start_idx:end_idx]
            
            ax2.plot(time_axis, preprocessed, 'g-', linewidth=1, label='预处理信号')
            ax2.set_title('预处理后EDA信号', fontsize=14, fontweight='bold')
            ax2.set_xlabel('时间 (秒)', fontsize=12)
            ax2.set_ylabel('电导值 (μS)', fontsize=12)
            ax2.grid(True, alpha=0.3)
            ax2.legend()
            
        else:
            fig, ax = plt.subplots(1, 1, figsize=self.figsize, dpi=self.dpi)
            ax.plot(time_axis, signal, 'b-', linewidth=1, label='EDA信号')
            ax.set_title('EDA信号', fontsize=14, fontweight='bold')
            ax.set_xlabel('时间 (秒)', fontsize=12)
            ax.set_ylabel('电导值 (μS)', fontsize=12)
            ax.grid(True, alpha=0.3)
            ax.legend()
        
        plt.tight_layout()
        
        # 保存图形
        if save_path:
            plt.savefig(save_path, dpi=self.dpi, bbox_inches='tight')
            logger.info(f"EDA信号图已保存: {save_path}")
            return save_path
        
        return None
    
    def _plot_eda_signal_plotly(self, eda_data, results, show_preprocessing, time_range, save_path):
        """使用Plotly绘制EDA信号"""
        signal = eda_data['signal']
        sampling_rate = eda_data.get('sampling_rate', 4.0)
        
        # 创建时间轴
        time_axis = np.arange(len(signal)) / sampling_rate
        
        # 应用时间范围
        if time_range:
            start_idx = int(time_range[0] * sampling_rate)
            end_idx = int(time_range[1] * sampling_rate)
            time_axis = time_axis[start_idx:end_idx]
            signal = signal[start_idx:end_idx]
        
        # 创建子图
        if show_preprocessing and results and 'preprocessed_signal' in results:
            fig = sp.make_subplots(
                rows=2, cols=1,
                subplot_titles=('原始EDA信号', '预处理后EDA信号'),
                vertical_spacing=0.1
            )
            
            # 原始信号
            fig.add_trace(
                go.Scatter(x=time_axis, y=signal, mode='lines', name='原始信号', 
                          line=dict(color='blue', width=1)),
                row=1, col=1
            )
            
            # 预处理信号
            preprocessed = results['preprocessed_signal']
            if time_range:
                preprocessed = preprocessed[start_idx:end_idx]
            
            fig.add_trace(
                go.Scatter(x=time_axis, y=preprocessed, mode='lines', name='预处理信号',
                          line=dict(color='green', width=1)),
                row=2, col=1
            )
            
            fig.update_xaxes(title_text="时间 (秒)", row=2, col=1)
            fig.update_yaxes(title_text="电导值 (μS)", row=1, col=1)
            fig.update_yaxes(title_text="电导值 (μS)", row=2, col=1)
            
        else:
            fig = go.Figure()
            fig.add_trace(
                go.Scatter(x=time_axis, y=signal, mode='lines', name='EDA信号',
                          line=dict(color='blue', width=1))
            )
            fig.update_layout(
                title='EDA信号',
                xaxis_title='时间 (秒)',
                yaxis_title='电导值 (μS)'
            )
        
        fig.update_layout(
            height=600,
            showlegend=True,
            template='plotly_white'
        )
        
        # 保存图形
        if save_path:
            plotly_plot(fig, filename=save_path, auto_open=False)
            logger.info(f"EDA信号图已保存: {save_path}")
            return save_path
        
        return None
    
    def plot_peak_detection(self,
                           eda_data: Dict[str, Any],
                           results: Dict[str, Any],
                           time_range: Tuple[float, float] = None,
                           save_path: str = None) -> Optional[str]:
        """绘制峰值检测结果
        
        Args:
            eda_data: EDA数据字典
            results: 处理结果字典
            time_range: 时间范围
            save_path: 保存路径
            
        Returns:
            保存的文件路径
        """
        if 'peaks' not in results or not results['peaks']:
            logger.warning("没有峰值检测结果可以绘制")
            return None
        
        signal = results.get('preprocessed_signal', eda_data['signal'])
        sampling_rate = eda_data.get('sampling_rate', 4.0)
        peaks = results['peaks']
        
        # 创建时间轴
        time_axis = np.arange(len(signal)) / sampling_rate
        
        # 应用时间范围
        if time_range:
            start_idx = int(time_range[0] * sampling_rate)
            end_idx = int(time_range[1] * sampling_rate)
            time_axis = time_axis[start_idx:end_idx]
            signal = signal[start_idx:end_idx]
            
            # 过滤峰值
            peaks = [p for p in peaks if time_range[0] <= p['onset_time'] <= time_range[1]]
        
        if self.use_plotly:
            return self._plot_peak_detection_plotly(time_axis, signal, peaks, save_path)
        else:
            return self._plot_peak_detection_matplotlib(time_axis, signal, peaks, save_path)
    
    def _plot_peak_detection_matplotlib(self, time_axis, signal, peaks, save_path):
        """使用Matplotlib绘制峰值检测结果"""
        fig, ax = plt.subplots(1, 1, figsize=self.figsize, dpi=self.dpi)
        
        # 绘制信号
        ax.plot(time_axis, signal, 'b-', linewidth=1, alpha=0.7, label='EDA信号')
        
        # 绘制峰值
        for i, peak in enumerate(peaks):
            onset_time = peak['onset_time']
            peak_time = peak['peak_time']
            recovery_time = peak.get('recovery_time')
            
            # 峰值起始点
            ax.axvline(onset_time, color='green', linestyle='--', alpha=0.7, 
                      label='峰值起始' if i == 0 else "")
            
            # 峰值点
            ax.axvline(peak_time, color='red', linestyle='-', alpha=0.8,
                      label='峰值点' if i == 0 else "")
            
            # 恢复点
            if recovery_time:
                ax.axvline(recovery_time, color='orange', linestyle=':', alpha=0.7,
                          label='恢复点' if i == 0 else "")
            
            # 峰值区域阴影
            if recovery_time:
                ax.axvspan(onset_time, recovery_time, alpha=0.1, color='gray')
        
        ax.set_title(f'EDA峰值检测结果 (检测到 {len(peaks)} 个峰值)', 
                    fontsize=14, fontweight='bold')
        ax.set_xlabel('时间 (秒)', fontsize=12)
        ax.set_ylabel('电导值 (μS)', fontsize=12)
        ax.grid(True, alpha=0.3)
        ax.legend()
        
        plt.tight_layout()
        
        # 保存图形
        if save_path:
            plt.savefig(save_path, dpi=self.dpi, bbox_inches='tight')
            logger.info(f"峰值检测图已保存: {save_path}")
            return save_path
        
        return None
    
    def _plot_peak_detection_plotly(self, time_axis, signal, peaks, save_path):
        """使用Plotly绘制峰值检测结果"""
        fig = go.Figure()
        
        # 绘制信号
        fig.add_trace(
            go.Scatter(x=time_axis, y=signal, mode='lines', name='EDA信号',
                      line=dict(color='blue', width=1))
        )
        
        # 绘制峰值
        for peak in peaks:
            onset_time = peak['onset_time']
            peak_time = peak['peak_time']
            recovery_time = peak.get('recovery_time')
            
            # 峰值起始点
            fig.add_vline(x=onset_time, line_dash="dash", line_color="green",
                         annotation_text="起始")
            
            # 峰值点
            fig.add_vline(x=peak_time, line_dash="solid", line_color="red",
                         annotation_text="峰值")
            
            # 恢复点
            if recovery_time:
                fig.add_vline(x=recovery_time, line_dash="dot", line_color="orange",
                             annotation_text="恢复")
        
        fig.update_layout(
            title=f'EDA峰值检测结果 (检测到 {len(peaks)} 个峰值)',
            xaxis_title='时间 (秒)',
            yaxis_title='电导值 (μS)',
            height=500,
            template='plotly_white'
        )
        
        # 保存图形
        if save_path:
            plotly_plot(fig, filename=save_path, auto_open=False)
            logger.info(f"峰值检测图已保存: {save_path}")
            return save_path
        
        return None
    
    def plot_frequency_analysis(self,
                               eda_data: Dict[str, Any],
                               results: Dict[str, Any],
                               save_path: str = None) -> Optional[str]:
        """绘制频域分析图
        
        Args:
            eda_data: EDA数据字典
            results: 处理结果字典
            save_path: 保存路径
            
        Returns:
            保存的文件路径
        """
        signal = results.get('preprocessed_signal', eda_data['signal'])
        sampling_rate = eda_data.get('sampling_rate', 4.0)
        
        # 计算功率谱密度
        from scipy import signal as scipy_signal
        
        frequencies, psd = scipy_signal.welch(signal, fs=sampling_rate, nperseg=min(256, len(signal)//4))
        
        if self.use_plotly:
            return self._plot_frequency_analysis_plotly(frequencies, psd, save_path)
        else:
            return self._plot_frequency_analysis_matplotlib(frequencies, psd, save_path)
    
    def _plot_frequency_analysis_matplotlib(self, frequencies, psd, save_path):
        """使用Matplotlib绘制频域分析"""
        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=self.figsize, dpi=self.dpi)
        
        # 功率谱密度
        ax1.semilogy(frequencies, psd, 'b-', linewidth=1)
        ax1.set_title('EDA信号功率谱密度', fontsize=14, fontweight='bold')
        ax1.set_xlabel('频率 (Hz)', fontsize=12)
        ax1.set_ylabel('功率谱密度', fontsize=12)
        ax1.grid(True, alpha=0.3)
        
        # 频率分布（线性尺度）
        ax2.plot(frequencies, psd, 'r-', linewidth=1)
        ax2.set_title('EDA信号频率分布', fontsize=14, fontweight='bold')
        ax2.set_xlabel('频率 (Hz)', fontsize=12)
        ax2.set_ylabel('功率', fontsize=12)
        ax2.grid(True, alpha=0.3)
        
        plt.tight_layout()
        
        # 保存图形
        if save_path:
            plt.savefig(save_path, dpi=self.dpi, bbox_inches='tight')
            logger.info(f"频域分析图已保存: {save_path}")
            return save_path
        
        return None
    
    def _plot_frequency_analysis_plotly(self, frequencies, psd, save_path):
        """使用Plotly绘制频域分析"""
        fig = sp.make_subplots(
            rows=2, cols=1,
            subplot_titles=('EDA信号功率谱密度', 'EDA信号频率分布'),
            vertical_spacing=0.1
        )
        
        # 功率谱密度（对数尺度）
        fig.add_trace(
            go.Scatter(x=frequencies, y=psd, mode='lines', name='功率谱密度',
                      line=dict(color='blue', width=1)),
            row=1, col=1
        )
        
        # 频率分布（线性尺度）
        fig.add_trace(
            go.Scatter(x=frequencies, y=psd, mode='lines', name='频率分布',
                      line=dict(color='red', width=1)),
            row=2, col=1
        )
        
        fig.update_yaxes(type="log", row=1, col=1)
        fig.update_xaxes(title_text="频率 (Hz)", row=2, col=1)
        fig.update_yaxes(title_text="功率谱密度", row=1, col=1)
        fig.update_yaxes(title_text="功率", row=2, col=1)
        
        fig.update_layout(
            height=600,
            showlegend=True,
            template='plotly_white'
        )
        
        # 保存图形
        if save_path:
            plotly_plot(fig, filename=save_path, auto_open=False)
            logger.info(f"频域分析图已保存: {save_path}")
            return save_path
        
        return None
    
    def plot_quality_assessment(self,
                               results: Dict[str, Any],
                               save_path: str = None) -> Optional[str]:
        """绘制质量评估图
        
        Args:
            results: 处理结果字典
            save_path: 保存路径
            
        Returns:
            保存的文件路径
        """
        if 'quality_assessment' not in results:
            logger.warning("没有质量评估结果可以绘制")
            return None
        
        quality = results['quality_assessment']
        
        if self.use_plotly:
            return self._plot_quality_assessment_plotly(quality, save_path)
        else:
            return self._plot_quality_assessment_matplotlib(quality, save_path)
    
    def _plot_quality_assessment_matplotlib(self, quality, save_path):
        """使用Matplotlib绘制质量评估"""
        # 提取质量指标
        metrics = {}
        for key, value in quality.items():
            if isinstance(value, (int, float)) and 0 <= value <= 1:
                metrics[key] = value
        
        if not metrics:
            logger.warning("没有有效的质量指标可以绘制")
            return None
        
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=self.figsize, dpi=self.dpi)
        
        # 雷达图
        angles = np.linspace(0, 2 * np.pi, len(metrics), endpoint=False).tolist()
        values = list(metrics.values())
        labels = list(metrics.keys())
        
        # 闭合雷达图
        angles += angles[:1]
        values += values[:1]
        
        ax1.plot(angles, values, 'o-', linewidth=2, color='blue')
        ax1.fill(angles, values, alpha=0.25, color='blue')
        ax1.set_xticks(angles[:-1])
        ax1.set_xticklabels(labels, fontsize=10)
        ax1.set_ylim(0, 1)
        ax1.set_title('质量评估雷达图', fontsize=14, fontweight='bold')
        ax1.grid(True)
        
        # 柱状图
        bars = ax2.bar(range(len(metrics)), values[:-1], color='skyblue', alpha=0.7)
        ax2.set_xticks(range(len(metrics)))
        ax2.set_xticklabels(labels, rotation=45, ha='right', fontsize=10)
        ax2.set_ylim(0, 1)
        ax2.set_ylabel('质量分数', fontsize=12)
        ax2.set_title('质量评估柱状图', fontsize=14, fontweight='bold')
        ax2.grid(True, alpha=0.3)
        
        # 添加数值标签
        for bar, value in zip(bars, values[:-1]):
            ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
                    f'{value:.2f}', ha='center', va='bottom', fontsize=9)
        
        plt.tight_layout()
        
        # 保存图形
        if save_path:
            plt.savefig(save_path, dpi=self.dpi, bbox_inches='tight')
            logger.info(f"质量评估图已保存: {save_path}")
            return save_path
        
        return None
    
    def _plot_quality_assessment_plotly(self, quality, save_path):
        """使用Plotly绘制质量评估"""
        # 提取质量指标
        metrics = {}
        for key, value in quality.items():
            if isinstance(value, (int, float)) and 0 <= value <= 1:
                metrics[key] = value
        
        if not metrics:
            logger.warning("没有有效的质量指标可以绘制")
            return None
        
        fig = sp.make_subplots(
            rows=1, cols=2,
            specs=[[{"type": "polar"}, {"type": "xy"}]],
            subplot_titles=('质量评估雷达图', '质量评估柱状图')
        )
        
        # 雷达图
        labels = list(metrics.keys())
        values = list(metrics.values())
        
        fig.add_trace(
            go.Scatterpolar(
                r=values,
                theta=labels,
                fill='toself',
                name='质量分数'
            ),
            row=1, col=1
        )
        
        # 柱状图
        fig.add_trace(
            go.Bar(
                x=labels,
                y=values,
                name='质量分数',
                marker_color='skyblue'
            ),
            row=1, col=2
        )
        
        fig.update_layout(
            polar=dict(
                radialaxis=dict(
                    visible=True,
                    range=[0, 1]
                )
            ),
            height=500,
            template='plotly_white'
        )
        
        # 保存图形
        if save_path:
            plotly_plot(fig, filename=save_path, auto_open=False)
            logger.info(f"质量评估图已保存: {save_path}")
            return save_path
        
        return None
    
    def create_analysis_report(self,
                              eda_data: Dict[str, Any],
                              results: Dict[str, Any],
                              output_dir: str,
                              report_name: str = "eda_analysis_report") -> str:
        """创建完整的EDA分析报告
        
        Args:
            eda_data: EDA数据字典
            results: 处理结果字典
            output_dir: 输出目录
            report_name: 报告名称
            
        Returns:
            报告目录路径
        """
        output_path = Path(output_dir) / report_name
        output_path.mkdir(parents=True, exist_ok=True)
        
        logger.info(f"开始创建EDA分析报告: {output_path}")
        
        # 生成各种图表
        plots = {}
        
        # 1. EDA信号图
        signal_path = output_path / "eda_signal.png"
        plots['signal'] = self.plot_eda_signal(eda_data, results, save_path=str(signal_path))
        
        # 2. 峰值检测图
        if 'peaks' in results and results['peaks']:
            peaks_path = output_path / "peak_detection.png"
            plots['peaks'] = self.plot_peak_detection(eda_data, results, save_path=str(peaks_path))
        
        # 3. 频域分析图
        freq_path = output_path / "frequency_analysis.png"
        plots['frequency'] = self.plot_frequency_analysis(eda_data, results, save_path=str(freq_path))
        
        # 4. 质量评估图
        if 'quality_assessment' in results:
            quality_path = output_path / "quality_assessment.png"
            plots['quality'] = self.plot_quality_assessment(results, save_path=str(quality_path))
        
        # 5. 生成HTML报告
        html_report = self._generate_html_report(eda_data, results, plots)
        html_path = output_path / "report.html"
        
        with open(html_path, 'w', encoding='utf-8') as f:
            f.write(html_report)
        
        logger.info(f"EDA分析报告创建完成: {output_path}")
        return str(output_path)
    
    def _generate_html_report(self, eda_data, results, plots):
        """生成HTML格式的分析报告"""
        html_template = """
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>EDA数据分析报告</title>
            <style>
                body {{ font-family: 'Microsoft YaHei', Arial, sans-serif; margin: 20px; }}
                .header {{ text-align: center; color: #2c3e50; }}
                .section {{ margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }}
                .metrics {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; }}
                .metric {{ background: #f8f9fa; padding: 10px; border-radius: 3px; }}
                .plot {{ text-align: center; margin: 20px 0; }}
                .plot img {{ max-width: 100%; height: auto; }}
                table {{ width: 100%; border-collapse: collapse; }}
                th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
                th {{ background-color: #f2f2f2; }}
            </style>
        </head>
        <body>
            <div class="header">
                <h1>EDA数据分析报告</h1>
                <p>生成时间: {timestamp}</p>
            </div>
            
            <div class="section">
                <h2>数据概览</h2>
                <div class="metrics">
                    <div class="metric">
                        <strong>信号长度:</strong> {signal_length} 个采样点
                    </div>
                    <div class="metric">
                        <strong>采样率:</strong> {sampling_rate} Hz
                    </div>
                    <div class="metric">
                        <strong>信号时长:</strong> {duration:.2f} 秒
                    </div>
                    <div class="metric">
                        <strong>质量评分:</strong> {quality_score:.2f}
                    </div>
                </div>
            </div>
            
            {signal_plot}
            
            {peaks_section}
            
            {frequency_plot}
            
            {quality_plot}
            
            <div class="section">
                <h2>分析指标</h2>
                {metrics_table}
            </div>
            
        </body>
        </html>
        """
        
        # 准备数据
        signal_length = len(eda_data['signal'])
        sampling_rate = eda_data.get('sampling_rate', 4.0)
        duration = signal_length / sampling_rate
        quality_score = results.get('quality_assessment', {}).get('overall_score', 0.0)
        
        # 信号图
        signal_plot = ""
        if plots.get('signal'):
            signal_plot = f"""
            <div class="section">
                <h2>EDA信号</h2>
                <div class="plot">
                    <img src="{Path(plots['signal']).name}" alt="EDA信号图">
                </div>
            </div>
            """
        
        # 峰值检测部分
        peaks_section = ""
        if plots.get('peaks'):
            peak_count = len(results.get('peaks', []))
            peaks_section = f"""
            <div class="section">
                <h2>峰值检测结果</h2>
                <p>检测到 <strong>{peak_count}</strong> 个SCR峰值</p>
                <div class="plot">
                    <img src="{Path(plots['peaks']).name}" alt="峰值检测图">
                </div>
            </div>
            """
        
        # 频域分析图
        frequency_plot = ""
        if plots.get('frequency'):
            frequency_plot = f"""
            <div class="section">
                <h2>频域分析</h2>
                <div class="plot">
                    <img src="{Path(plots['frequency']).name}" alt="频域分析图">
                </div>
            </div>
            """
        
        # 质量评估图
        quality_plot = ""
        if plots.get('quality'):
            quality_plot = f"""
            <div class="section">
                <h2>质量评估</h2>
                <div class="plot">
                    <img src="{Path(plots['quality']).name}" alt="质量评估图">
                </div>
            </div>
            """
        
        # 指标表格
        metrics_table = self._generate_metrics_table(results.get('metrics', {}))
        
        return html_template.format(
            timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            signal_length=signal_length,
            sampling_rate=sampling_rate,
            duration=duration,
            quality_score=quality_score,
            signal_plot=signal_plot,
            peaks_section=peaks_section,
            frequency_plot=frequency_plot,
            quality_plot=quality_plot,
            metrics_table=metrics_table
        )
    
    def _generate_metrics_table(self, metrics):
        """生成指标表格HTML"""
        if not metrics:
            return "<p>无可用指标</p>"
        
        table_html = "<table><tr><th>指标名称</th><th>数值</th><th>单位</th></tr>"
        
        for key, value in metrics.items():
            if isinstance(value, (int, float)):
                table_html += f"<tr><td>{key}</td><td>{value:.4f}</td><td>-</td></tr>"
        
        table_html += "</table>"
        return table_html

# 便捷函数
def plot_eda_signal(eda_data: Dict[str, Any], 
                   results: Dict[str, Any] = None,
                   save_path: str = None,
                   **kwargs) -> Optional[str]:
    """绘制EDA信号的便捷函数"""
    visualizer = EDAVisualizer(**kwargs)
    return visualizer.plot_eda_signal(eda_data, results, save_path=save_path)

def plot_peak_detection(eda_data: Dict[str, Any],
                       results: Dict[str, Any],
                       save_path: str = None,
                       **kwargs) -> Optional[str]:
    """绘制峰值检测结果的便捷函数"""
    visualizer = EDAVisualizer(**kwargs)
    return visualizer.plot_peak_detection(eda_data, results, save_path=save_path)

def create_analysis_report(eda_data: Dict[str, Any],
                          results: Dict[str, Any],
                          output_dir: str,
                          **kwargs) -> str:
    """创建完整的EDA分析报告"""
    visualizer = EDAVisualizer()
    return visualizer.create_analysis_report(eda_data, results, output_dir)


def plot_eda_analysis(eda_data: Dict[str, Any],
                     results: Dict[str, Any],
                     save_path: str = None,
                     **kwargs) -> Optional[str]:
    """绘制完整的EDA分析图表
    
    Args:
        eda_data: EDA数据字典
        results: EDA分析结果字典
        save_path: 保存路径
        **kwargs: 额外的绘图参数
        
    Returns:
        保存的图片路径（如果指定了save_path）
    """
    if not MATPLOTLIB_AVAILABLE:
        logger.error("matplotlib未安装，无法绘制图表")
        return None
    
    try:
        # 创建可视化器实例
        visualizer = EDAVisualizer()
        
        # 创建综合分析图表
        fig, axes = plt.subplots(2, 2, figsize=(15, 10))
        fig.suptitle('EDA信号综合分析', fontsize=16, fontweight='bold')
        
        # 获取信号数据
        signal = eda_data.get('signal', [])
        sampling_rate = eda_data.get('sampling_rate', 4.0)
        
        if len(signal) == 0:
            logger.error("信号数据为空")
            return None
        
        # 创建时间轴
        time_axis = np.arange(len(signal)) / sampling_rate
        
        # 1. 原始信号图
        axes[0, 0].plot(time_axis, signal, 'b-', linewidth=1, alpha=0.8)
        axes[0, 0].set_title('原始EDA信号')
        axes[0, 0].set_xlabel('时间 (秒)')
        axes[0, 0].set_ylabel('电导值 (μS)')
        axes[0, 0].grid(True, alpha=0.3)
        
        # 2. 峰值检测结果
        if 'peaks' in results and results['peaks']:
            peaks = results['peaks']
            axes[0, 1].plot(time_axis, signal, 'b-', linewidth=1, alpha=0.6, label='原始信号')
            
            # 绘制峰值点
            if 'peak_indices' in peaks:
                peak_indices = peaks['peak_indices']
                if len(peak_indices) > 0:
                    axes[0, 1].scatter(time_axis[peak_indices], signal[peak_indices], 
                                     color='red', s=50, zorder=5, label='检测到的峰值')
            
            axes[0, 1].set_title(f'峰值检测结果 (检测到 {len(peaks.get("peak_indices", []))} 个峰值)')
            axes[0, 1].set_xlabel('时间 (秒)')
            axes[0, 1].set_ylabel('电导值 (μS)')
            axes[0, 1].legend()
            axes[0, 1].grid(True, alpha=0.3)
        else:
            axes[0, 1].plot(time_axis, signal, 'b-', linewidth=1)
            axes[0, 1].set_title('峰值检测结果 (无峰值检测数据)')
            axes[0, 1].set_xlabel('时间 (秒)')
            axes[0, 1].set_ylabel('电导值 (μS)')
            axes[0, 1].grid(True, alpha=0.3)
        
        # 3. 统计指标柱状图
        if 'metrics' in results:
            metrics = results['metrics']
            metric_names = []
            metric_values = []
            
            # 选择主要指标进行展示
            key_metrics = ['mean', 'std', 'peak_count', 'peak_rate']
            for key in key_metrics:
                if key in metrics:
                    metric_names.append(key)
                    metric_values.append(metrics[key])
            
            if metric_names:
                bars = axes[1, 0].bar(metric_names, metric_values, color=['skyblue', 'lightgreen', 'orange', 'pink'])
                axes[1, 0].set_title('主要统计指标')
                axes[1, 0].set_ylabel('数值')
                
                # 在柱状图上添加数值标签
                for bar, value in zip(bars, metric_values):
                    height = bar.get_height()
                    axes[1, 0].text(bar.get_x() + bar.get_width()/2., height,
                                   f'{value:.3f}', ha='center', va='bottom')
            else:
                axes[1, 0].text(0.5, 0.5, '无统计指标数据', ha='center', va='center', 
                              transform=axes[1, 0].transAxes, fontsize=12)
                axes[1, 0].set_title('统计指标')
        else:
            axes[1, 0].text(0.5, 0.5, '无统计指标数据', ha='center', va='center', 
                          transform=axes[1, 0].transAxes, fontsize=12)
            axes[1, 0].set_title('统计指标')
        
        # 4. 质量评估
        if 'quality_assessment' in results:
            quality = results['quality_assessment']
            quality_score = quality.get('overall_score', 0)
            
            # 创建质量评估饼图
            labels = ['良好', '需要改进']
            sizes = [quality_score * 100, (1 - quality_score) * 100]
            colors = ['lightgreen', 'lightcoral']
            
            axes[1, 1].pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
            axes[1, 1].set_title(f'信号质量评估 (总分: {quality_score:.2f})')
        else:
            axes[1, 1].text(0.5, 0.5, '无质量评估数据', ha='center', va='center', 
                          transform=axes[1, 1].transAxes, fontsize=12)
            axes[1, 1].set_title('信号质量评估')
        
        # 调整布局
        plt.tight_layout()
        
        # 保存图片
        if save_path:
            save_path = Path(save_path)
            save_path.parent.mkdir(parents=True, exist_ok=True)
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
            logger.info(f"EDA分析图表已保存到: {save_path}")
            
            # 如果不在交互模式，关闭图形
            if not plt.isinteractive():
                plt.close(fig)
            
            return str(save_path)
        else:
            plt.show()
            return None
            
    except Exception as e:
        logger.error(f"绘制EDA分析图表失败: {str(e)}")
        return None